﻿// *********************************************************************
// [DCOM Productions]
// [Copyright (C) DCOM Productions All rights reserved.]
// *********************************************************************

namespace BitFlex.Windows.Forms {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Data;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using BitFlex.Windows.Forms.Design;

    /// <summary>
    /// Provides a UI for forward and backward wizard-style navigation between pages
    /// </summary>
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(Wizard), "Resources.UIWizardToolboxBitmap.bmp")]
    [Designer(typeof(BitFlex.Windows.Forms.Design.WizardDesigner))]
    public partial class Wizard : UserControl {
        /// <summary>
        /// Initializes a new instance of the BitFlex.Windows.Forms.Wizard class
        /// </summary>
        public Wizard() {
            InitializeComponent();
        }

        #region Events

        /// <summary>
        /// Triggered when the wizard is cancelled
        /// </summary>
        [Category("Action")]
        [Description("Triggered when the wizard is cancelled")]
        public event System.EventHandler<EventArgs> Cancelled;
        /// <summary>
        /// EventHandler Callback for <see cref="Cancelled"/> event
        /// </summary>
        /// <param name="sender">The sender object</param>
        /// <param name="e">The event arguments</param>
        protected virtual void OnCancelled(object sender, System.EventArgs e) {
            if (Cancelled != null) {
                Cancelled(sender, e);
            }
        }

        /// <summary>
        /// Triggered when the wizard is finished
        /// </summary>
        [Category("Action")]
        [Description("Triggered when the wizard is finished")]
        public event System.EventHandler<EventArgs> Finished;
        /// <summary>
        /// EventHandler Callback for <see cref="Finished"/> event
        /// </summary>
        /// <param name="sender">The sender object</param>
        /// <param name="e">The event arguments</param>
        protected virtual void OnFinished(object sender, System.EventArgs e) {
            if (Finished != null) {
                Finished(sender, e);
            }
        }

        /// <summary>
        /// Triggered when a page is changed in the wizard, usually when moving back a page, or forward a page
        /// </summary>
        [Category("Action")]
        [Description("Triggered when a page is changed in the wizard, usually when moving back a page, or forward a page")]
        public event System.EventHandler<WizardPageChangedEventArgs> PageChanged;
        /// <summary>
        /// EventHandler Callback for <see cref="PageChanged"/> event
        /// </summary>
        /// <param name="sender">The sender object</param>
        /// <param name="e">The event arguments</param>
        protected virtual void OnPageChanged(object sender, BitFlex.Windows.Forms.WizardPageChangedEventArgs e) {
            if (PageChanged != null) {
                PageChanged(sender, e);
            }
        }

        /// <summary>
        /// Triggered when the wizard is changing from one page to another
        /// </summary>
        [Category("Action")]
        [Description("Triggered when the wizard is changing from one page to another")]
        public event System.EventHandler<WizardPageChangingEventArgs> PageChanging;
        /// <summary>
        /// EventHandler Callback for <see cref="PageChanging"/> event
        /// </summary>
        /// <param name="sender">The sender object</param>
        /// <param name="e">The event arguments</param>
        protected virtual void OnPageChanging(object sender, BitFlex.Windows.Forms.WizardPageChangingEventArgs e) {
            if (PageChanging != null) {
                PageChanging(sender, e);
            }
        }

        #endregion

        #region Properties

        private WizardPageCollection<WizardPage> m_Pages = null;
        /// <summary>
        /// Gets or sets the collection of pages in the wizard
        /// </summary>
        [Category("Data")]
        [Description("Gets or sets the collection of pages in the wizard")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Editor(typeof(WizardPageCollectionEditor), typeof(UITypeEditor))]
        public WizardPageCollection<WizardPage> Pages {
            get {
                if (m_Pages == null) {
                    m_Pages = new WizardPageCollection<WizardPage>();
                    m_Pages.OwnerWizard = this;
                }
                return m_Pages;
            }
        }

        private int m_SelectedIndex = -1;
        /// <summary>
        /// Gets or sets the selected index in the wizard
        /// </summary>
        [Browsable(false)]
        [Category("Data")]
        [DefaultValue(-1)]
        [Description("Gets or sets the selected index in the wizard")]
        public int SelectedIndex {
            get {
                return m_SelectedIndex;
            }
            set {
                int oldIndex = m_SelectedIndex;
                int newIndex = value;
                WizardPageChangingEventArgs e = new WizardPageChangingEventArgs(oldIndex, newIndex);
                OnPageChanging(this, e);
                if (e.Cancel) {
                    return;
                }
                if (value > Pages.Count - 1) {
                    throw new System.ArgumentOutOfRangeException();
                }
                else if (value < -1) {
                    throw new System.ArgumentOutOfRangeException();
                }
                else if (value == -1) {
                    m_SelectedIndex = value;
                    EnsureDesignerEmpty();
                    OnPageChanged(this, new WizardPageChangedEventArgs(oldIndex, newIndex));
                }
                else {
                    m_SelectedIndex = value;
                    EnsureDesignerVisible(Pages[value]);
                    OnPageChanged(this, new WizardPageChangedEventArgs(oldIndex, newIndex));
                    if (oldIndex >= 0) 
                        Pages[oldIndex].OnNavigatingFrom(this, EventArgs.Empty); // left old page
                    Pages[newIndex].OnNavigatedTo(this, EventArgs.Empty); // displayed new
                }
            }
        }

        /// <summary>
        /// Gets or sets the selected page in the wizard
        /// </summary>
        [Browsable(false)]
        [Category("Data")]
        [Description("Gets or sets the selected page in the wizard")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public WizardPage SelectedPage {
            get {
                if (SelectedIndex == -1) {
                    return null;
                }
                else {
                    return Pages[SelectedIndex];
                }
            }
            set {
                if (value == null) {
                    SelectedIndex = -1;
                }
                else if (Pages.Contains(value)) {
                    SelectedIndex = Pages.IndexOf(value);
                }
                else {
                    throw new System.ArgumentException("The specified WizardPage is not owned by this wizard");
                }
            }
        }

        private Image m_Watermark = null;
        /// <summary>
        /// Gets or sets the image watermark of the wizard page
        /// </summary>
        [Category("Appearance")]
        [DefaultValue(null)]
        [Description("Gets or sets the image watermark of the wizard page")]
        public Image Watermark {
            get {
                return m_Watermark;
            }
            set {
                m_Watermark = value;
                Invalidate();
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Navigates to the previous page in the wizard
        /// </summary>
        public void Back() {
            if (Pages.Count > 1 && SelectedIndex > 0) {
                SelectedIndex--;
            }
            EnsureButtonStates();
        }

        private void Back(object sender, EventArgs e) {
            Back();
        }

        /// <summary>
        /// Cancels the wizard and raises the <see cref="Cancelled"/> event
        /// </summary>
        public void Cancel() {
            EventArgs e = new EventArgs();
            OnCancelled(this, e);
        }

        private void Cancel(object sender, EventArgs e) {
            Cancel();
        }

        private void EnsureButtonStates() {
            uxBack.Enabled = SelectedIndex == 0 ? false : true;
            uxNext.Enabled = SelectedIndex == Pages.Count - 1 ? false : SelectedPage.Index < Pages.Count - 1 ? true : false;
            uxOK.Enabled = SelectedIndex == Pages.Count - 1 ? true : false;
            // validation
            if (uxNext.Enabled) {
                if (SelectedPage != null && SelectedPage.EnableValidation) {
                    if (SelectedPage.IsValid) {
                        uxNext.Enabled = true;
                    }
                    else {
                        uxNext.Enabled = false;
                    }
                }
            }
            if (uxOK.Enabled) {
                if (SelectedPage != null && SelectedPage.EnableValidation) {
                    if (SelectedPage.IsValid) {
                        uxOK.Enabled = true;
                    }
                    else {
                        uxOK.Enabled = false;
                    }
                }
            }
            // overrides
            if (!m_BackButtonEnabled) uxBack.Enabled = false;
            if (!m_CancelButtonEnabled) uxCancel.Enabled = false;
            if (!m_NextButtonEnabled) uxNext.Enabled = false;
        }

        private void EnsureDesignerEmpty() {
            foreach (Control control in Controls.OfType<WizardPage>()) {
                control.Visible = false;
            }
        }

        private void EnsureDesignerVisible(WizardPage page) {
            foreach (Control control in Controls.OfType<WizardPage>()) {
                control.Visible = false;
            }
            page.Dock = DockStyle.Fill;
            page.SendToBack();
            page.Visible = true;
        }

        /// <summary>
        /// Finishes the wizard and raises the <see cref="Finished"/> event
        /// </summary>
        public void Finish() {
            EventArgs e = new EventArgs();
            OnFinished(this, e);
        }

        private void Finish(object sender, EventArgs e) {
            Finish();
        }

        /// <summary>
        /// Navigates to the first page in the wizard
        /// </summary>
        public void First() {
            if (Pages.Count > 0) {
                SelectedIndex = 0;
            }
            EnsureButtonStates();
        }

        /// <summary>
        /// Navigates to the last page in the wizard
        /// </summary>
        public void Last() {
            if (Pages.Count > 0) {
                SelectedIndex = Pages.Count - 1;
            }
            EnsureButtonStates();
        }

        /// <summary>
        /// Navigates to the next page in the wizard
        /// </summary>
        public void Next() {
            if (Pages.Count > 1 && SelectedIndex > -1 && SelectedIndex < Pages.Count - 1) {
                SelectedIndex++;
            }
            EnsureButtonStates();
        }

        private void Next(object sender, EventArgs e) {
            Next();
        }

        //protected override void OnControlAdded(ControlEventArgs e) {
        //    base.OnControlAdded(e);
        //    WizardPage page = e.Control as WizardPage;
        //    if (page == null) {
        //        throw new System.ArgumentException("Only a WizardPage control can be added to a Wizard");
        //    }
        //    else {
        //        if (!Pages.Contains(page)) {
        //            Pages.Add(page);
        //        }
        //        EnsureDesignerVisible(page);
        //    }
        //}

        //protected override void OnControlRemoved(ControlEventArgs e) {
        //    base.OnControlRemoved(e);
        //    WizardPage page = e.Control as WizardPage;
        //    if (page != null && Pages.Contains(page)) {
        //        Pages.Remove(page);
        //    }
        //}

        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            if (DesignMode) {
                Padding = new Padding(3);
            }
            else {
                //Padding = new Padding(0);
            }
            if (Pages.Count > 0) {
                SelectedIndex = 0;
            }
            EnsureButtonStates();
        }

        private bool m_BackButtonEnabled = true;
        /// <summary>
        /// Sets the state of the back navigation button in the wizard
        /// </summary>
        /// <param name="enabled">Optional: true to enable, false to disable. Default value is true.</param>
        public void SetBackState(bool enabled = false) {
            m_BackButtonEnabled = enabled;
            EnsureButtonStates();
        }

        private bool m_CancelButtonEnabled = true;
        /// <summary>
        /// Sets the state of the cancel navigation button in the wizard
        /// </summary>
        /// <param name="enabled">Optional: true to enable, false to disable. Default value is true.</param>
        public void SetCancelButtonState(bool enabled = false) {
            m_CancelButtonEnabled = enabled;
            EnsureButtonStates();
        }

        private bool m_NextButtonEnabled = true;
        /// <summary>
        /// Sets the state of the next navigation button in the wizard
        /// </summary>
        /// <param name="enabled">Optional: true to enable, false to disable. Default value is true.</param>
        public void SetNextState(bool enabled = false) {
            m_NextButtonEnabled = enabled;
            EnsureButtonStates();
        }

        #endregion
    }
}